/**
 * 
 */
package truerefactor.graph;

import java.util.ArrayList;
import java.util.List;

/**
 * AttributeNode - An implementation of CodeNode which represents some attribute
 * of a Class
 * 
 * @author Isaac Griffith
 */
public class AttributeNode extends CodeNode {

    /**
     * the simple name of the attribute, not the fully qualified name
     */
    private String baseID;
    /**
     * The ClassNode to which this AttributeNode belongs
     */
    private ClassNode parent;
    /**
     * The Type ClassNode of this AttributeNode
     */
    private ClassNode associatedClass;
    /**
     * boolean flag that determines if this is a primitive or no
     */
    private boolean primitive;
    /**
     * The Accessibility type of this attribute node
     */
    private Accessibility accessibility;
    /**
     * The simple name for the type of this AttributeNode
     */
    private String type;
    /**
     * boolean flag that determines if this is a static attribute
     */
    private boolean isStatic;
    /**
     * boolean flag that determines if this is a final attribute
     */
    private boolean isFinal;
    /**
     * list of methods in the parent class that reference this attribute
     */
    private List<MethodNode> associatedClassMethods;
    /**
     * list of methods in other classes that reference this attribute
     */
    private List<MethodNode> associatedOtherMethods;

    /**
     * @return the parent class node for this attribute node
     */
    public ClassNode getParent()
    {
        return parent;
    }

    /**
     * Sets the ClassNode to which this AttributeNode belongs
     * 
     * @param parent
     *            the new parent ClassNode
     */
    public void setParent(ClassNode parent)
    {
        this.parent = parent;
        if (parent != null)
            this.identifier = parent.getIdentifier() + "." + baseID;
        else
            this.identifier = baseID;
    }

    /**
     * @return the associatedClass or type class node for this attribute node
     */
    public ClassNode getAssociatedClass()
    {
        return associatedClass;
    }

    /**
     * @param associatedClass
     *            the new associated class for this attribute node
     */
    public void setAssociatedClass(ClassNode associatedClass)
    {
        this.associatedClass = associatedClass;
    }

    /**
     * @return true if this attribute node is a primitive, false otherwise
     */
    public boolean isPrimitive()
    {
        return primitive;
    }

    /**
     * @param primitive
     *            set to true if this is a primitive attribute, and false
     *            otherwise
     */
    public void setPrimitive(boolean primitive)
    {
        this.primitive = primitive;
    }

    /**
     * Creates a new AttributeNode for the given parent ClassNode, with the
     * provided identifier and code
     * 
     * @param parent
     *            Parent ClassNode
     * @param identifier
     *            attribute identifier
     * @param code
     *            attribute code
     */
    public AttributeNode(ClassNode parent, String identifier, String code)
    {
        this(CodeGraph.nextUID(), parent, identifier, code);
    }

    /**
     * Creates a new instance of AttributeNode with the given unique id, parent
     * ClassNode, simple name, and code
     * 
     * @param uid
     *            the unique ID of this attributeNode
     * @param parent
     *            the parent ClassNode
     * @param baseID
     *            the simple name of this attribute
     * @param code
     *            attribute code
     */
    public AttributeNode(long uid, ClassNode parent, String baseID, String code)
    {
        super(uid, baseID, code);

        this.accessibility = Accessibility.Public;
        this.associatedClass = null;
        this.primitive = false;
        this.isFinal = false;
        this.type = "";
        this.baseID = baseID;
        this.isStatic = false;
        this.associatedClassMethods = new ArrayList<MethodNode>();
        this.associatedOtherMethods = new ArrayList<MethodNode>();
        this.setParent(parent);
    }

    /**
     * @return true if this attribute is static, false otherwise
     */
    public boolean isStatic()
    {
        return isStatic;
    }

    /**
     * sets this attributes static flag to be the value of the provided boolean
     * 
     * @param isStatic
     *            static flag
     */
    public void setStatic(boolean isStatic)
    {
        this.isStatic = isStatic;
    }

    /**
     * @return the name of the Type of this attribute
     */
    public String getType()
    {
        return type;
    }

    /**
     * Sets the name of the type of this attribute to be the provided type
     * 
     * @param type
     *            new type name for this attribute
     */
    public void setType(String type)
    {
        this.type = type;
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.graph.CodeNode#add(truerefactor.graph.CodeNode)
     */
    @Override
    public void add(CodeNode child)
    {
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.graph.CodeNode#remove(truerefactor.graph.CodeNode)
     */
    @Override
    public void remove(CodeNode child)
    {
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.graph.CodeNode#getChild(java.lang.String)
     */
    @Override
    public CodeNode getChild(String identfier)
    {
        return null;
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.graph.CodeNode#getChildren()
     */
    @Override
    public List<CodeNode> getChildren()
    {
        return new ArrayList<CodeNode>();
    }

    /**
     * @return List of MethodNodes that reference this attribute from within the
     *         attributes parent ClassNode
     */
    public List<MethodNode> getAssociatedMethods()
    {
        return associatedClassMethods;
    }

    /**
     * Adds an associated method, from within the same parent class, to this
     * attribute node
     * 
     * @param associate
     *            associated methodNode to be added
     */
    public void addAssociatedClassMethod(MethodNode associate)
    {
        if (!associatedClassMethods.contains(associate) && associate.getParentClass().equals(this.parent))
        {
            associatedClassMethods.add(associate);
        }
    }

    /**
     * Adds an associated method, from a different parent class, to this
     * attribute node
     * 
     * @param associate
     *            associated other methodnode to be added
     */
    public void addAssociatedOtherMethod(MethodNode associate)
    {
        if (!associatedOtherMethods.contains(associate) && !associate.getParentClass().equals(this.parent))
            associatedOtherMethods.add(associate);
    }

    /**
     * Removes a method reference from within this attributes parent class
     * 
     * @param associate method to be removed
     */
    public void removeAssociatedClassMethod(MethodNode associate)
    {
        if (associatedClassMethods.contains(associate))
            associatedClassMethods.remove(associate);
    }

    /**
     * removes a method that references this attribute from outside it's parent
     * class
     * 
     * @param associate
     *            method to be removed
     */
    public void removeAssociatedOtherMethod(MethodNode associate)
    {
        if (associatedOtherMethods.contains(associate))
            associatedOtherMethods.remove(associate);
    }

    /**
     * @return list of method's referencing this attribute from outside it's
     *         parent class
     */
    public List<MethodNode> getOtherMethods()
    {
        return associatedOtherMethods;
    }

    /**
     * @return this methods accessibility
     */
    public Accessibility getAccessibility()
    {
        return accessibility;
    }

    /**
     * Sets the Accessibility of this attribute to be the provided value
     * 
     * @param visibility
     *            new accessibility
     */
    public void setAccessibility(Accessibility visibility)
    {
        this.accessibility = visibility;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        AttributeNode node = new AttributeNode(uid, null, new String(baseID), new String(code));
        node.setType(new String(type));
        node.setAccessibility(accessibility);
        node.setPrimitive(primitive);
        node.setStatic(isStatic);

        return node;
    }

    /**
     * @return the baseID
     */
    public String getBaseID()
    {
        return baseID;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString()
    {
        return getBaseID();
    }

    /**
     * Sets this attributes final flag to be the value of the provided boolean
     * 
     * @param b
     *            final flag
     */
    public void setFinal(boolean b)
    {
        this.isFinal = b;
    }

    /**
     * @return true if this attribute is final, false otherwise
     */
    public boolean isFinal()
    {
        return this.isFinal;
    }
}
